package com.kai.service.article.facade;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.kai.service.article.mapper.ArticleOperateRecordMapper;
import com.kai.service.article.service.IArticleCommentLikeRecordService;
import com.kai.service.article.service.IArticleCommentService;
import com.kai.service.article.service.IArticleContentService;
import com.kai.service.article.service.IArticleService;
import com.kai.service.data.request.article.*;
import com.kai.service.data.response.article.ArticleCommentResponse;
import com.kai.service.data.response.article.ArticleDetailResponse;
import com.kai.service.data.response.article.ArticleResponse;
import com.kai.service.data.response.user.UserInfoResponse;
import com.kai.service.enums.ArticleOperateTypeEnum;
import com.kai.service.enums.ArticleStatusEnum;
import com.kai.service.enums.DeleteStatusEnum;
import com.kai.service.enums.ResultCodeEnum;
import com.kai.service.exception.GenericException;
import com.kai.service.rabbitmq.constant.RabbitmqConstant;
import com.kai.service.user.entity.User;
import com.kai.service.user.entity.UserFocus;
import com.kai.service.user.service.IUserFocusService;
import com.kai.service.user.service.IUserService;
import com.kai.service.article.entity.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ArticleFacade {

    @Autowired
    private IArticleService articleService;

    @Autowired
    private IArticleContentService articleContentService;

    @Autowired
    private IUserService userService;

    @Autowired
    private IArticleCommentService articleCommentService;

    @Resource
    private ArticleOperateRecordMapper articleOperateRecordMapper;

    @Autowired
    private IArticleCommentLikeRecordService articleCommentLikeRecordService;

    @Autowired
    private IUserFocusService userFocusService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * 新增文章
     *
     * @param createArticleRequest
     * @return
     */
    @Transactional(rollbackFor = Throwable.class)
    public Boolean createArticle(CreateArticleRequest createArticleRequest) {
        Date nowDate = new Date();
        Article article = new Article();
        BeanUtils.copyProperties(createArticleRequest, article);
        article.setCategoryLabel(Joiner.on(",").join(createArticleRequest.getCategoryLabel()))
                .setCreateDatetime(nowDate)
                .setUpdateDatetime(nowDate)
                .setIsDeleted(DeleteStatusEnum.NO.getCode());
        articleService.save(article);

        Long articleId = article.getId();
        ArticleContent articleContent = new ArticleContent()
                .setArticleId(articleId)
                .setContent(createArticleRequest.getContent())
                .setUpdateDatetime(nowDate);
        articleContentService.save(articleContent);

        return Boolean.TRUE;
    }

    /**
     * 编辑文章
     *
     * @param articleId
     * @param editorArticleRequest
     * @return
     */
    @Transactional(rollbackFor = Throwable.class)
    public Boolean editorArticle(Long articleId, EditorArticleRequest editorArticleRequest) {
        Date nowDate = new Date();
        List<Integer> categoryLabel = editorArticleRequest.getCategoryLabel();
        Article article = articleService.getById(articleId);
        if (null == article) {
            throw new GenericException(ResultCodeEnum.ARTICLE_NOT_EXIST);
        }
        BeanUtils.copyProperties(editorArticleRequest, article);
        article.setCategoryLabel(Joiner.on(",").join(categoryLabel)).setId(articleId).setUpdateDatetime(nowDate).updateById();

        if (StringUtils.isNotBlank(editorArticleRequest.getContent())) {
            ArticleContent articleContent = new ArticleContent();
            articleContent.setArticleId(articleId).setContent(editorArticleRequest.getContent())
                    .setUpdateDatetime(nowDate).updateById();
        }

        return Boolean.TRUE;
    }

    /**
     * 分页查询文章
     *
     * @param pagingArticleRequest
     * @return
     */
    public IPage<ArticleResponse> pagingArticle(PagingArticleRequest pagingArticleRequest) {
        Long userId = pagingArticleRequest.getUserId();
        Long authorId = pagingArticleRequest.getAuthorId();
        Integer columnId = pagingArticleRequest.getColumnId();
        String title = pagingArticleRequest.getTitle();
        String categoryLabel = pagingArticleRequest.getCategoryLabel();
        String status = pagingArticleRequest.getStatus();
        Date startTime = pagingArticleRequest.getStartTime();
        Date endTime = pagingArticleRequest.getEndTime();

        if (Objects.equals(status, ArticleStatusEnum.DRAFT.getDescription()) && null == authorId) {
            throw new GenericException(ResultCodeEnum.PERMISSION_DENIED);
        }

        IPage<Article> articlePage = articleService.lambdaQuery()
                .eq(Article::getIsDeleted, DeleteStatusEnum.NO.getCode())
                .eq(Objects.nonNull(authorId), Article::getAuthorId, authorId)
                .eq(Objects.nonNull(columnId), Article::getColumnId, columnId)
                .eq(StringUtils.isNotBlank(status), Article::getStatus, status)
                .like(StringUtils.isNotBlank(title), Article::getTitle, title)
                .like(StringUtils.isNotBlank(categoryLabel), Article::getCategoryLabel, categoryLabel)
                .ge(Objects.nonNull(startTime), Article::getCreateDatetime, startTime)
                .le(Objects.nonNull(endTime), Article::getCreateDatetime, endTime)
                .orderByDesc(Article::getCreateDatetime)
                .page(new Page<>(pagingArticleRequest.getPageIndex(), pagingArticleRequest.getPageSize()));

        List<ArticleResponse> responses = articleConversionArticleResponse(userId, articlePage.getRecords());

        return new Page<ArticleResponse>()
                .setRecords(responses)
                .setTotal(articlePage.getTotal())
                .setSize(articlePage.getSize())
                .setCurrent(articlePage.getCurrent())
                .setPages(articlePage.getPages());
    }

    /**
     * 分页查询历史文章
     *
     * @param pagingHistoryArticleRequest
     * @return
     */
    public IPage<ArticleResponse> historyPagingArticle(PagingHistoryArticleRequest pagingHistoryArticleRequest) {
        Integer pageIndex = pagingHistoryArticleRequest.getPageIndex();
        Integer pageSize = pagingHistoryArticleRequest.getPageSize();
        Long userId = pagingHistoryArticleRequest.getUserId();

        IPage<ArticleOperateRecord> articleOperateRecordPage = articleOperateRecordMapper.selectPage(new Page<>(pageIndex, pageSize), new LambdaQueryWrapper<ArticleOperateRecord>()
                .eq(ArticleOperateRecord::getUserId, userId)
                .eq(ArticleOperateRecord::getOperateType, pagingHistoryArticleRequest.getOperateType())
                .orderByDesc(ArticleOperateRecord::getCreateDatetime));

        List<ArticleResponse> responses = Lists.newArrayList();
        List<ArticleOperateRecord> records = articleOperateRecordPage.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            List<Long> articleIds = records.stream().map(ArticleOperateRecord::getArticleId).collect(Collectors.toList());
            List<Article> articles = articleService.lambdaQuery().in(Article::getId, articleIds).list();

            responses = articleConversionArticleResponse(userId, articles);
        }

        return new Page<ArticleResponse>()
                .setRecords(responses)
                .setPages(articleOperateRecordPage.getPages())
                .setTotal(articleOperateRecordPage.getTotal())
                .setSize(articleOperateRecordPage.getSize())
                .setCurrent(articleOperateRecordPage.getCurrent());
    }

    /**
     * 获取热门文章
     *
     * @return
     */
    public List<ArticleResponse> getHotArticle(Long userId) {
        // TODO: 2022/4/20 热门文章查询待优化
        List<Long> articleIds = articleOperateRecordMapper.hotArticleIds();

        List<Article> articles = articleService.lambdaQuery().in(Article::getId, articleIds).list();
        return articleConversionArticleResponse(userId, articles);
    }

    /**
     * 查看文章内容
     *
     * @param articleId articleId 文章id
     * @return 是否操作成功
     */
    public ArticleDetailResponse articleDetail(Long articleId) {
        ArticleContent articleContent = articleContentService.getById(articleId);
        if (null == articleContent) {
            throw new GenericException(ResultCodeEnum.ARTICLE_NOT_EXIST);
        }
        ArticleDetailResponse articleDetailResponse = new ArticleDetailResponse();
        BeanUtils.copyProperties(articleContent, articleDetailResponse);

        return articleDetailResponse;
    }

    /**
     * 删除文章
     *
     * @param articleId 文章id
     * @return 是否操作成功
     */
    public Boolean deleteArticle(Long articleId) {
        Article article = articleService.getById(articleId);
        if (null == article) {
            throw new GenericException(ResultCodeEnum.ARTICLE_NOT_EXIST);
        }

        return article.setIsDeleted(DeleteStatusEnum.YES.getCode()).updateById();
    }

    /**
     * 文章操作
     *
     * @param request
     * @return
     */
    public Boolean articleOperate(ArticleOperatorRequest request) {
        rabbitTemplate.convertAndSend(RabbitmqConstant.DIRECT_EXCHANGE_NAME, RabbitmqConstant.ARTICLE_OPERATOR_ROUTING_KEY, request);

        return Boolean.TRUE;
    }

    /**
     * 新增评论
     *
     * @param articleId 文章id
     * @param articleCommentRequest 评论内容请求参数，评论人和评论内容不能为空
     * @return 是否操作成功
     */
    public Boolean articleComment(Long articleId, ArticleCommentRequest articleCommentRequest) {
        Date nowDate = new Date();
        Article article = articleService.getById(articleId);
        if (null == article){
            throw new GenericException(ResultCodeEnum.ARTICLE_NOT_EXIST);
        }
        ArticleComment articleComment = new ArticleComment();
        BeanUtils.copyProperties(articleCommentRequest, articleComment);
        articleComment.setArticleId(articleId).setCreateDatetime(nowDate).setUpdateDatetime(nowDate);

        return articleCommentService.save(articleComment);
    }

    /**
     * 分页查看文章评论
     *
     * @param articleId
     * @param pagingArticleCommentRequest
     * @return
     */
    public IPage<ArticleCommentResponse> pagingArticleComment(Long articleId, PagingArticleCommentRequest pagingArticleCommentRequest) {
        Integer commentId = pagingArticleCommentRequest.getCommentId();
        Long userId = pagingArticleCommentRequest.getUserId();

        Article article = articleService.getById(articleId);
        if (null == article){
            throw new GenericException(ResultCodeEnum.ARTICLE_NOT_EXIST);
        }
        IPage<ArticleComment> page = new Page<>(pagingArticleCommentRequest.getPageIndex(), pagingArticleCommentRequest.getPageSize());

        IPage<ArticleComment> articleCommentPage = articleCommentService.page(page, new LambdaQueryWrapper<ArticleComment>()
                .orderByDesc(ArticleComment::getCreateDatetime)
                .eq(ArticleComment::getArticleId, articleId)
                .eq(ArticleComment::getCommentId, commentId));

        List<ArticleCommentResponse> articleCommentResponseList = articleCommentPage.getRecords().stream().map(articleComment -> {
            ArticleCommentResponse articleCommentResponse = new ArticleCommentResponse();
            BeanUtils.copyProperties(articleComment, articleCommentResponse);

            // 评论点赞数量
            articleCommentResponse.setLike(articleCommentLikeRecordService.count(new LambdaQueryWrapper<ArticleCommentLikeRecord>().eq(ArticleCommentLikeRecord::getCommentId, articleComment.getId())));

            // 查询评论人信息
            User fromUser = userService.getById(articleComment.getFromUserId());
            articleCommentResponse.setFromNickName(fromUser.getNickName());
            articleCommentResponse.setFromImgUrl(fromUser.getImgUrl());

            // 查询接收评论人信息
            if (!Objects.equals(-1L, articleComment.getToUserId())){
                User toUser = userService.getById(articleComment.getToUserId());
                articleCommentResponse.setToNickName(toUser.getNickName());
                articleCommentResponse.setToImgUrl(toUser.getImgUrl());
            }else {
                // 查询子评论条数
                int count = articleCommentService.count(new LambdaQueryWrapper<ArticleComment>().eq(ArticleComment::getCommentId, articleComment.getId()));
                articleCommentResponse.setSubNumber(count);
            }
            // 当前用户是否点赞此评论
            ArticleCommentLikeRecord articleCommentLikeRecord = articleCommentLikeRecordService.getOne(new LambdaQueryWrapper<ArticleCommentLikeRecord>()
                    .eq(ArticleCommentLikeRecord::getUserId, userId)
                    .eq(ArticleCommentLikeRecord::getCommentId, articleComment.getId()));
            if (null != articleCommentLikeRecord){
                articleCommentResponse.setIsLike(Boolean.TRUE);
            }

            return articleCommentResponse;
        }).collect(Collectors.toList());

        IPage<ArticleCommentResponse> articleCommentResponsePage = new Page<>();
        articleCommentResponsePage.setRecords(articleCommentResponseList)
                .setPages(articleCommentPage.getPages())
                .setCurrent(articleCommentPage.getCurrent())
                .setSize(articleCommentPage.getSize())
                .setTotal(articleCommentPage.getTotal());

        return articleCommentResponsePage;
    }

    /**
     * 评论点赞
     *
     * @param userId
     * @param commentId
     * @param isLike
     * @return
     */
    public Boolean likeComment(Long userId, Integer commentId, Boolean isLike) {
        Date nowDate = new Date();
        ArticleComment articleComment = articleCommentService.getById(commentId);
        if (null == articleComment){
            throw new GenericException(ResultCodeEnum.COMMENT_NOT_EXIST);
        }
        if (isLike){
            articleCommentLikeRecordService.remove(new LambdaQueryWrapper<ArticleCommentLikeRecord>().eq(ArticleCommentLikeRecord::getUserId, userId)
                    .eq(ArticleCommentLikeRecord::getCommentId, commentId));
        } else {
            ArticleCommentLikeRecord articleCommentLikeRecord = new ArticleCommentLikeRecord();
            articleCommentLikeRecord.setCommentId(commentId).setUserId(userId).setCreateDatetime(nowDate);
            articleCommentLikeRecordService.save(articleCommentLikeRecord);
        }
        articleComment.setUpdateDatetime(nowDate).updateById();

        return Boolean.TRUE;
    }

    /**
     * 删除评论
     *
     * @param commentId
     * @return
     */
    @Transactional(rollbackFor = Throwable.class)
    public Boolean deleteComment(Integer commentId) {
        articleCommentService.removeById(commentId);
        articleCommentService.remove(new LambdaQueryWrapper<ArticleComment>().eq(ArticleComment::getCommentId, commentId));
        articleCommentLikeRecordService.remove(new LambdaQueryWrapper<ArticleCommentLikeRecord>().eq(ArticleCommentLikeRecord::getCommentId, commentId));

        return Boolean.TRUE;
    }

    /**
     * 文章转换为文章响应对象
     *
     * @param userId
     * @param articles
     * @return
     */
    private List<ArticleResponse> articleConversionArticleResponse(Long userId, List<Article> articles) {
        List<ArticleResponse> responses = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(articles)) {
            Set<Long> authorIds = articles.stream().map(Article::getAuthorId).collect(Collectors.toSet());
            List<User> users = userService.lambdaQuery().in(User::getId, authorIds).list();
            Map<Long, UserInfoResponse> userInfoResponseMap = users.stream().map(UserInfoResponse::new).collect(Collectors.toMap(UserInfoResponse::getId, userInfoResponse -> userInfoResponse));

            Map<Long, Article> articleMap = articles.stream().collect(Collectors.toMap(Article::getId, article -> article));
            Set<Long> articleIds = articleMap.keySet();
            responses = articleOperateRecordMapper.countByArticleIds(articleIds);
            responses.forEach(articleResponse -> {
                Article article = articleMap.get(articleResponse.getId());
                String label = article.getCategoryLabel();

                if (StringUtils.isNotBlank(label)){
                    String[] labelArray = label.split(",");
                    List<Integer> categoryLabelList = Arrays.stream(labelArray).map(Integer::valueOf).collect(Collectors.toList());
                    articleResponse.setCategoryLabel(categoryLabelList);
                }

                articleResponse.setColumnId(article.getColumnId())
                        .setAuthorId(article.getAuthorId())
                        .setUserInfo(userInfoResponseMap.get(article.getAuthorId()))
                        .setTitle(article.getTitle())
                        .setSummary(article.getSummary())
                        .setSummaryImgUrl(article.getSummaryImgUrl())
                        .setStatus(article.getStatus())
                        .setCreateDatetime(article.getCreateDatetime());
            });

            if (Objects.nonNull(userId)) {
                List<Long> attentionAuthorIds = userFocusService.lambdaQuery()
                        .select(UserFocus::getBloggerId)
                        .eq(UserFocus::getFansId, userId)
                        .in(UserFocus::getBloggerId, authorIds)
                        .list()
                        .stream().map(UserFocus::getBloggerId).collect(Collectors.toList());

                List<ArticleOperateRecord> articleOperateRecords = articleOperateRecordMapper.selectList(Wrappers.lambdaQuery(new ArticleOperateRecord())
                        .eq(ArticleOperateRecord::getUserId, userId)
                        .in(ArticleOperateRecord::getArticleId, articleIds));

                Map<Long, Set<String>> articleOperateMap = articleOperateRecords.stream().collect(Collectors.groupingBy(ArticleOperateRecord::getArticleId, Collectors.mapping(ArticleOperateRecord::getOperateType, Collectors.toSet())));

                responses.forEach(response -> {
                    Set<String> articleOperate = articleOperateMap.get(response.getId());
                    if (!CollectionUtils.isEmpty(articleOperate)) {
                        response.setIsFocus(attentionAuthorIds.contains(response.getAuthorId()))
                                .setIsLike(articleOperate.contains(ArticleOperateTypeEnum.LIKE.name()))
                                .setIsNoLike(articleOperate.contains(ArticleOperateTypeEnum.NOLIKE.name()))
                                .setIsCollect(articleOperate.contains(ArticleOperateTypeEnum.COLLECT.name()));
                    }
                });
            }
        }

        return responses;
    }
}
